﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.Statistics.RandomNumberGenerator
{
    /// <summary>
    /// This class provides the generation of random numbers by using the XorShift method.
    /// </summary>
    [Serializable]
    public class XORShiftRandomGenerator : AbstractRandomGenerator
    {
        /// <summary>
        /// This field holds an internal value for random number generation.
        /// </summary>
        private ulong x;

        /// <summary>
        /// This field holds an internal value for random number generation.
        /// </summary>
        private ulong y;

        /// <summary>
        /// This field holds an internal value for random number generation.
        /// </summary>
        private ulong z;

        /// <summary>
        /// This field holds an internal value for random number generation.
        /// </summary>
        private ulong w;

        /// <summary>
        /// Initializes a new instance of the <see cref="XORShiftRandomGenerator"/> class.
        /// </summary>
        public XORShiftRandomGenerator()
            : base((uint) DateTime.Now.Millisecond)
        {
            this.x = this.Seed;
            this.y = 362436069;
            this.z = 521288629;
            this.w = 88675123;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="XORShiftRandomGenerator"/> class.
        /// </summary>
        /// <param name="seed">A number used to calculate a starting value for the pseudo-random number sequence.
        /// If a negative number is specified, the absolute value of the number is used.</param>
        public XORShiftRandomGenerator(uint seed)
            : base(seed)
        {
            this.x = seed;
            this.y = 362436069;
            this.z = 521288629;
            this.w = 88675123;
        }

        /// <summary>
        /// Returns a nonnegative random number.
        /// </summary>
        /// <returns>
        /// A 32-bit unsigned integer greater than or equal to zero and less than than maximum integer value.
        /// </returns>
        public override int Next()
        {
            ulong t = this.x ^ (this.x << 11);

            this.x = y;
            this.y = z;
            this.z = w;
            this.w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));

            return ((int) (this.w%int.MaxValue));
        }

        /// <summary>
        /// Nexts the specified max value.
        /// </summary>
        /// <param name="maxValue">Returns a nonnegative random number less than the specified maximum.</param>
        /// <returns>
        /// A 32-bit signed integer greater than or equal to zero and less than maximum integer value.
        /// </returns>
        public override int Next(int maxValue)
        {
            return this.Next(0, maxValue);
        }

        /// <summary>
        /// Returns a random number within a specified range.
        /// </summary>
        /// <param name="minValue">The inclusive lower bound of the random number returned.</param>
        /// <param name="maxValue">The exclusive upper bound of the random number returned. maxValue must be
        /// greater than or equal to minValue.</param>
        /// <returns>
        /// A 32-bit signed integer greater than or equal to zero and less than maximum integer value.
        /// </returns>
        public override int Next(int minValue, int maxValue)
        {
            if (minValue < 0)
            {
                minValue = 0;
            }

            if (maxValue == int.MaxValue)
            {
                maxValue -= 1;
            }

            if (minValue > maxValue)
            {
                int tempuri = maxValue;

                maxValue = minValue;
                minValue = tempuri;
            }

            return (int) (Math.Floor((maxValue - minValue + 1)*this.NextDouble() + minValue));
        }

        /// <summary>
        /// Returns a random number between 0.0 and 1.0.
        /// </summary>
        /// <returns>
        /// A double-precision floating point number greater than or equal to 0.0, and less
        /// than 1.0.
        /// </returns>
        public override double NextDouble()
        {
            return (double)this.Next()/int.MaxValue;
        }
    }
}